package com.abetsy.vben.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.abetsy.vben.entity.constant.TMDBConstant;
import com.abetsy.vben.entity.util.ProxyInfo;
import com.abetsy.vben.param.DramaSharePreviewParam;
import com.abetsy.vben.service.PreviewStrategy;
import com.abetsy.vben.service.TMDBService;
import com.abetsy.vben.utils.i18n.I18nUtil;
import com.abetsy.vben.utils.i18n.VbenException;
import com.abetsy.vben.vo.FileDetailVo;
import com.abetsy.vben.vo.ShareDramaInfoVo;
import lombok.SneakyThrows;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Component
public class QuarkpanPreviewStrategy implements PreviewStrategy {

    @Resource
    private TMDBService tmdbService;

    private static final String MKV_MARK = "video/x-matroska";

    private static final String MP4_MARK = "video/mp4";

    private static final String HEADER_HOST = "drive-m.quark.cn";

    private static final String HEADER_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36";

    private static final String SHARE_URL_HEADER = "https://pan.quark.cn/s/";

    private static final String CHECK_S_TOKEN_URL = "https://drive-m.quark.cn/1/clouddrive/share/sharepage/token?fr=h5&pr=ucpro";

    private static final String FILE_LIST_URL = "https://drive-m.quark.cn/1/clouddrive/share/sharepage/detail" +
            "?pr=ucpro&fr=pc&pwd_id=%s&pdir_fid=%s&force=0&_page=%d&_size=50" +
            "&_fetch_banner=0&_fetch_share=0&_fetch_total=1&_sort=file_type:asc&stoken=";

    @Override
    public ShareDramaInfoVo previewDramaShare(DramaSharePreviewParam sharePreviewParam) {
        // 先判断是不是夸克网盘的资源链接
        checkResourceLink(sharePreviewParam);
        // 是否缺少必要的参数
        ShareDramaInfoVo shareKeys = checkResourceParam(sharePreviewParam);
        // 组装资源请求列表
        List<FileDetailVo> fileDetails = collectFileList(sharePreviewParam, shareKeys);
        // 开始匹配TMDB数据
        matchTmDBName(sharePreviewParam, fileDetails);
        // 检查资源的匹配状态
        checkResourceStatus(fileDetails, shareKeys);
        // 整理数据
        fileDetails.sort(Comparator.comparing(FileDetailVo::getSeasonNum).reversed());
        shareKeys.setFileDetailList(fileDetails);
        return shareKeys;
    }

    private void checkResourceStatus(List<FileDetailVo> fileDetails, ShareDramaInfoVo shareKeys) {
        if (CollUtil.isNotEmpty(fileDetails)) {
            List<Integer> numLists = fileDetails.stream().map(FileDetailVo::getSeasonNum).collect(Collectors.toList());
            Map<Integer, Integer> countMap = CollUtil.countMap(numLists).entrySet().stream()
                    .filter(entry -> entry.getValue() > 1)
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            Collections.sort(numLists);
            int min = numLists.get(0);
            int max = numLists.get(numLists.size() - 1);
            shareKeys.setLastCount(max);
            FileDetailVo lastOne = fileDetails.stream().filter(x -> x.getSeasonNum().equals(max)).findFirst().orElse(null);
            shareKeys.setLastName( lastOne == null ? "" : lastOne.getTmdbName());
            String msg;
            if (min == max && numLists.size() == 1) {
                msg = I18nUtil.info("only.one.season", max);
                shareKeys.setMsg(msg);
                shareKeys.setStatus(true);
                return;
            } else {
                msg = I18nUtil.info("msg.season.show", min, max);
            }
            if (MapUtil.isNotEmpty(countMap)) {
                // 有重复的
                StringBuilder temp = new StringBuilder("{");
                for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
                    temp.append(I18nUtil.info("season.repeat", entry.getKey(), entry.getValue())).append(',');
                }
                temp.append("}");
                shareKeys.setMsg(String.format("%s,%s:%s", msg, I18nUtil.info("season.repeat.tips"), temp.toString().replace(",}", "}")));
                shareKeys.setStatus(false);
            } else {
                // 无重复的
                if (isConsecutive(numLists)) {
                    // 是连续的
                    shareKeys.setMsg(msg);
                    shareKeys.setStatus(true);
                } else {
                    // 不是连续的
                    String temp = findMissing(numLists).stream().map(Object::toString).collect(Collectors.joining(","));
                    shareKeys.setMsg(String.format("%s,%s", msg, I18nUtil.info("season.lack", temp)));
                    shareKeys.setStatus(false);
                }
            }
        } else {
            shareKeys.setMsg(I18nUtil.info("season.match.empty"));
            shareKeys.setStatus(false);
        }
    }

    public static boolean isConsecutive(List<Integer> numbers) {
        Collections.sort(numbers);
        int firstNumber = numbers.get(0);
        int lastNumber = numbers.get(numbers.size() - 1);
        return lastNumber - firstNumber == numbers.size() - 1;
    }

    public static List<Integer> findMissing(List<Integer> numbers) {
        List<Integer> missingNumbers = new ArrayList<>();
        List<Integer> sortedNumbers = new ArrayList<>(numbers);
        Collections.sort(sortedNumbers);
        int min = sortedNumbers.get(0);
        int max = sortedNumbers.get(sortedNumbers.size() - 1);
        for (int i = min + 1; i < max; i++) {
            if (!sortedNumbers.contains(i)) {
                missingNumbers.add(i);
            }
        }
        return missingNumbers;
    }

    private void matchTmDBName(DramaSharePreviewParam param, List<FileDetailVo> fileDetails) {
        Map<Integer, String> tmdbKV = new HashMap<>();
        try {
            String url = String.format(TMDBConstant.SEASON_DETAILS_KEY, param.getDramaId(), param.getSeasonId(), LocaleContextHolder.getLocale().getLanguage());
            HttpRequest httpRequest = HttpRequest.get(url);
            ProxyInfo proxyInfo = tmdbService.getProxyInfo();
            if (proxyInfo.isProxyStatus()) {
                httpRequest.setHttpProxy(proxyInfo.getProxyIp(), proxyInfo.getProxyPort());
            }
            HttpResponse execute = httpRequest.execute();
            JSONObject parseObj = JSONUtil.parseObj(execute.body());
            JSONArray jsonArray = JSONUtil.parseArray(parseObj.getByPath("episodes"));
            for (Object object : jsonArray) {
                JSONObject item = JSONUtil.parseObj(object);
                tmdbKV.put(item.getInt("episode_number"), item.getStr("name"));
            }
        } catch (Exception ignored) {}
        // 名称匹配
        if (!fileDetails.isEmpty() && !tmdbKV.isEmpty()) {
            for (FileDetailVo fileDetailVo : fileDetails) {
                String seasonName = fileDetailVo.getSeasonNum().toString();
                if (tmdbKV.containsKey(fileDetailVo.getSeasonNum())) {
                    seasonName = tmdbKV.get(fileDetailVo.getSeasonNum());
                }
                fileDetailVo.setTmdbName(seasonName);
            }
        }
    }

    @SneakyThrows
    private List<FileDetailVo> collectFileList(DramaSharePreviewParam sharePreviewParam, ShareDramaInfoVo shareKeys) {
        List<FileDetailVo> fileDetails = new ArrayList<>();
        int page = 1;
        while (true) {
            String filesUrl = String.format(FILE_LIST_URL, shareKeys.getPwdId(), shareKeys.getPdirFid(), page) + URLEncoder.encode(shareKeys.getStoken(), "UTF-8");
            HttpRequest filesRequest = HttpUtil.createGet(filesUrl);
            filesRequest.header("host", HEADER_HOST);
            filesRequest.header("user-agent", HEADER_USER_AGENT);
            String filesInfo = filesRequest.execute().body();
            JSONObject jsonObject = JSONUtil.parseObj(filesInfo);
            if (jsonObject.getInt("status") == 200 && jsonObject.getInt("code") == 0) {
                // 资源正常
                JSONObject dataObject = jsonObject.getJSONObject("data");
                JSONObject metaObject = jsonObject.getJSONObject("metadata");
                JSONArray jsonArray = dataObject.getJSONArray("list");
                if (jsonArray.isEmpty()) {
                    break;
                }
                DecimalFormat df = new DecimalFormat("#.##");
                for (Object object : jsonArray) {
                    JSONObject itemObject = JSONUtil.parseObj(object);
                    String formatType = itemObject.getStr("format_type");
                    if (CharSequenceUtil.equalsAny(formatType, MKV_MARK, MP4_MARK) && !sharePreviewParam.getVideoType().equalsIgnoreCase("no")){
                        if (sharePreviewParam.getVideoType().equalsIgnoreCase("mkv")
                                && !formatType.equalsIgnoreCase(MKV_MARK)) {
                            continue;
                        }
                        if (sharePreviewParam.getVideoType().equalsIgnoreCase("mp4")
                                && !formatType.equalsIgnoreCase(MP4_MARK)) {
                            continue;
                        }
                    }

                    if (CharSequenceUtil.equalsAny(formatType, MKV_MARK, MP4_MARK)) {
                        if (!sharePreviewParam.getVideoType().equalsIgnoreCase("no")) {
                            if (sharePreviewParam.getVideoType().equalsIgnoreCase("mkv")
                                    && !formatType.equalsIgnoreCase(MKV_MARK)) {
                                continue;
                            }
                            if (sharePreviewParam.getVideoType().equalsIgnoreCase("mp4")
                                    && !formatType.equalsIgnoreCase(MP4_MARK)) {
                                continue;
                            }
                        }
                        String resolutionStr = itemObject.getStr("video_width") + "x" + itemObject.getStr("video_height");
                        if (!sharePreviewParam.getVideoResolution().equalsIgnoreCase("no")
                                && !resolutionStr.equalsIgnoreCase(sharePreviewParam.getVideoResolution())) {
                            continue;
                        }
                        FileDetailVo fileDetail = new FileDetailVo();
                        String protoName = itemObject.getStr("file_name");
                        Pattern regex = Pattern.compile(sharePreviewParam.getReConfig());
                        Matcher matcher = regex.matcher(protoName);
                        if (matcher.find()) {
                            try {
                                int seasonNum = Integer.parseInt(matcher.group(1));
                                fileDetail.setSeasonNum(seasonNum);
                            } catch (NumberFormatException e) {
                                continue;
                            }
                        } else {
                            continue;
                        }
                        fileDetail.setDataSize(DataSizeUtil.format(itemObject.getLong("size")));
                        fileDetail.setVideoType(formatType.equalsIgnoreCase(MKV_MARK) ? "mkv" : "mp4");
                        fileDetail.setProtoName(protoName);
                        fileDetail.setDuration(DurationFormatUtils.formatDuration(itemObject.getLong("duration") * 1000, "HH:mm:ss"));
                        fileDetail.setVideoResolution(resolutionStr);
                        fileDetail.setVideoFps(df.format(itemObject.getDouble("fps")));
                        fileDetail.setCreatedAt(DateUtil.format(new Date(itemObject.getLong("created_at")), "yyyy-MM-dd HH:mm:ss"));
                        fileDetails.add(fileDetail);
                    }
                }
                if (fileDetails.size() >= metaObject.getInt("_total")) {
                    break;
                }
                page += 1;
            } else {
                // 资源异常
                VbenException.locale(jsonObject.getStr("message"));
            }
        }
        return fileDetails;
    }

    private void checkResourceLink(DramaSharePreviewParam sharePreviewParam) {
        if (CharSequenceUtil.isBlank(sharePreviewParam.getResourceLink())) {
            VbenException.locale("quarkpan.link.empty");
        }
        if (!sharePreviewParam.getResourceLink().startsWith(SHARE_URL_HEADER)) {
            VbenException.locale("quarkpan.link.header.error");
        }
    }

    private ShareDramaInfoVo checkResourceParam(DramaSharePreviewParam sharePreviewParam) {
        ShareDramaInfoVo shareKeys = new ShareDramaInfoVo();
        // 先检查分享码
        if (CharSequenceUtil.isBlank(sharePreviewParam.getResourceCode())) {
            shareKeys.setPasscode("");
        } else {
            shareKeys.setPasscode(sharePreviewParam.getResourceCode());
        }
        // 开始正式开始检查
        String pattern = "/s/(\\w+)";
        if (sharePreviewParam.getResourceLink().contains("#/list/share/")) {
            pattern = "/s/(\\w+)#/list/share/(\\w+)-";
        }
        Pattern regex = Pattern.compile(pattern);
        Matcher matcher = regex.matcher(sharePreviewParam.getResourceLink());
        if (matcher.find()) {
            int groupCount = matcher.groupCount();
            if (groupCount == 1) {
                shareKeys.setPwdId(matcher.group(1));
                shareKeys.setPdirFid("");
            }
            if (groupCount == 2) {
                shareKeys.setPwdId(matcher.group(1));
                shareKeys.setPdirFid(matcher.group(2));
            }
        } else {
            VbenException.locale("quarkpan.link.miss.pwd_id");
        }
        // 真正开始检查处理
        Map<String, Object> params = new HashMap<>();
        params.put("passcode", shareKeys.getPasscode());
        params.put("pwd_id", shareKeys.getPwdId());
        HttpRequest httpRequest = HttpUtil.createPost(CHECK_S_TOKEN_URL);
        httpRequest.header("host", HEADER_HOST);
        httpRequest.header("user-agent", HEADER_USER_AGENT);
        httpRequest.body(JSONUtil.toJsonStr(params));
        String infoToken = httpRequest.execute().body();
        JSONObject jsonObject = JSONUtil.parseObj(infoToken);
        if (jsonObject.getInt("status") == 200 && jsonObject.getInt("code") == 0) {
            // 资源正常
            JSONObject dataObject = jsonObject.getJSONObject("data");
            shareKeys.setStoken(dataObject.getStr("stoken"));
            shareKeys.setExpiredAt(DateUtil.format(new Date(dataObject.getLong("expired_at")), "yyyy-MM-dd"));
            // 分享人信息
            JSONObject authorObject = dataObject.getJSONObject("author");
            shareKeys.setAuthorType(authorObject.getStr("member_type"));
            shareKeys.setAuthorAvatar(authorObject.getStr("avatar_url"));
            shareKeys.setAuthorName(authorObject.getStr("nick_name"));
        } else {
            // 资源异常
            VbenException.locale(jsonObject.getStr("message"));
        }
        return shareKeys;
    }

    @Override
    public boolean supports(DramaSharePreviewParam sharePreviewParam) {
        return sharePreviewParam.getCloudType().equalsIgnoreCase("quarkpan");
    }

}
